package com.topnav.mc.admin.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.js.lib.result.Result;
import com.js.lib.utils.DateUtils;
import com.js.lib.utils.JsonUtil;
import com.topnav.mc.admin.bean.cachebean.CacheDeviceFenceBean;
import com.topnav.mc.admin.bean.cachebean.CacheFencePolygonBean;
import com.topnav.mc.admin.bean.cachebean.CacheOrgBean;
import com.topnav.mc.admin.bean.cachebean.EnumDeviceFenceStatus;
import com.topnav.mc.admin.entity.*;
import com.topnav.mc.admin.entity.bean.DeviceAlarmBean;
import com.topnav.mc.admin.entity.enums.DeviceAlarmCate;
import com.topnav.mc.admin.entity.enums.EnumCoordinateSystem;
import com.topnav.mc.admin.entity.param.DeviceAlarmParam;
import com.topnav.mc.admin.service.*;
import com.topnav.mc.schedule.job.ClearExpiredDataJob;
import com.topnav.mc.sse.DeviceAlarmQueue;
import com.topnav.mc.sse.EnumSseMessageType;
import com.topnav.mc.sse.MessageBean;
import com.topnav.mc.uitls.gps.Gps;
import com.topnav.mc.uitls.gps.GpsUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Author: kenny
 * @Date: 2024/1/9 9:52
 */
@CrossOrigin
@RestController
@Slf4j
public class TestController {
    @Resource
    ClearExpiredDataJob clearExpiredDataJob;

    @GetMapping("/test/clear/expired/data")
    public Result clearExpiredData() throws JobExecutionException {
        clearExpiredDataJob.execute(null);
        return Result.ok();
    }


    @GetMapping("/test/deferredresult")
    public DeferredResult<Result> playInvite() throws InterruptedException {
        String start = DateUtils.format(new Date(),"HH:mm:ss.SSS");
        DeferredResult<Result> result = new DeferredResult<Result>(5000L);
        result.onTimeout(()->{
            //System.out.println(DateUtils.format(new Date(),"HH:mm:ss.SSS"));
            result.setResult(Result.error(String.format("超时: %s==>%s",start,DateUtils.format(new Date(),"HH:mm:ss.SSS"))));
        });

        new Thread(()->{
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        ;

        return result;
    }

    /**
     * 围栏测试
     */
    @Autowired
    IDevicePositionService devicePositionService;
    @Autowired
    IOrganizationService organizationService;
    @Autowired
    IDeviceService deviceService;
    @Autowired
    IFenceService fenceService;
    @Autowired
    IDeviceChannelService channelService;
    @Autowired
    IDeviceAlarmService deviceAlarmService;
    @Autowired
    IFenceAlarmService fenceAlarmService;
    @Autowired
    IUserService userService;
    @Autowired
    private DeviceAlarmQueue deviceAlarmQueue;
    Integer repeatTimes = 2;


    @GetMapping("/test/fence")
    public Result FanceAngGps(@RequestBody Map<String,Object> param) throws InterruptedException {
        String deviceCode = (String)param.get("deviceCode");
        Date from = DateUtils.parse("2024-03-28 12:00:00","yyyy-MM-dd HH:mm:ss");
        Date to = DateUtils.parse("2024-03-28 14:00:00","yyyy-MM-dd HH:mm:ss");
        if (StringUtils.isEmpty(deviceCode)){
            deviceCode = "44030000491311000000";
        }
        if (!StringUtils.isEmpty((String)param.get("begin"))){
            from = DateUtils.parse((String)param.get("begin"),"yyyy-MM-dd HH:mm:ss");
        }
        if (!StringUtils.isEmpty((String)param.get("end"))){
            to = DateUtils.parse((String)param.get("end"),"yyyy-MM-dd HH:mm:ss");
        }
        LambdaQueryWrapper<DevicePosition> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DevicePosition::getDeviceCode,deviceCode);
        queryWrapper.ge(DevicePosition::getTime,from);
        queryWrapper.le(DevicePosition::getTime,to);
        List<DevicePosition> gpsList = devicePositionService.list(queryWrapper);

        Device device = deviceService.getFromCache(deviceCode);
        for(int i=0;i<gpsList.size();i++){
            fenceAlarm(device, gpsList.get(i),i);
        }

        return Result.ok(gpsList);
    }

    /**
     * 检查围栏报警
     * @param device
     * @param gpsMsgInfo
     * @throws InterruptedException
     */
    private void fenceAlarm(Device device,DevicePosition gpsMsgInfo,int index) throws InterruptedException {
        //围栏判断
        //当前设备所属部门
        CacheOrgBean orgPath = organizationService.getFromCache(device.getOrgId());
        //所有围栏，按部门的当前设备部门对比
        List<CacheFencePolygonBean> fencelist = fenceService.getAllFromCache();
        for(CacheFencePolygonBean fencebean : fencelist) {
            Gps gps = null;
            //按部门判断，设备只要属部门及下级就报警生效
            if (orgPath.getPath().startsWith(fencebean.getPath())) {
                //判断坐标系，不一样则转换
                if (!device.getCoordinateSystem().equals(fencebean.getCoordinateSystem())) {
                    if (fencebean.getCoordinateSystem().equals(EnumCoordinateSystem.GCJ02.getCode())) {
                        //目前只支持 WGS84和GCJ02 如果围标坐是 GCJ02则设备一定属于 WGS84
                        if (device.getCoordinateSystem().equals(EnumCoordinateSystem.WGS84.getCode())) {
                            gps = GpsUtils.gps84ToGcj02(gpsMsgInfo.getLatitude(), gpsMsgInfo.getLongitude());
                        } else {
                            log.error(">>>>> 暂不支持设备的坐标系: {}", device.getCoordinateSystem());
                        }
                    } else {
                        if (device.getCoordinateSystem().equals(EnumCoordinateSystem.GCJ02.getCode())) {
                            gps = GpsUtils.gcj02ToGps84(gpsMsgInfo.getLatitude(), gpsMsgInfo.getLongitude());
                        } else {
                            log.error(">>>>> 暂不支持设备的坐标系: {}", device.getCoordinateSystem());
                        }
                    }
                } else {
                    gps = new Gps(gpsMsgInfo.getLongitude(), gpsMsgInfo.getLatitude());
                }

                // 如果gps==null就不做报警处理
                if (gps != null) {

                    //是否保存有上次的数据
                    CacheDeviceFenceBean cdfBean = fenceService.getDeviceFenceStatusFromCache(device.getId().toString() + "-" + fencebean.getId().toString());
                    //当前是否在围栏内
                    Integer inPolyon = GpsUtils.PointInPolygon(gps, fencebean.getCoordinates()) == true ?
                            EnumDeviceFenceStatus.FENCEIN.getCode() : EnumDeviceFenceStatus.FENCEOUT.getCode();
                    //log.info("##=====>>> inPolyon:{}={}",inPolyon,cdfBean==null?null:cdfBean.getStatus());
                    if (cdfBean == null) {
                        //第一次上线，不报警，count=0, 在栏内或栏外同时多次后再报警
                        fenceService.setDeviceFenceStatusToCache(
                                new CacheDeviceFenceBean().setDeviceId(device.getId())
                                        .setFenceId(fencebean.getId())
                                        .setTime(new Date())
                                        .setStatus(inPolyon).setCount(0));

                    } else if (inPolyon != cdfBean.getStatus()) {
                        //第一次保存或第一次有变化重新保存
                        fenceService.setDeviceFenceStatusToCache(
                                new CacheDeviceFenceBean().setDeviceId(device.getId())
                                        .setFenceId(fencebean.getId())
                                        .setTime(new Date())
                                        .setStatus(inPolyon).setCount(1));
                    } else if (inPolyon == cdfBean.getStatus()) {
                        //没有变化, 累计两次才算是报警
                        log.info("##=====>>> 两次才算是报警:{}={}",cdfBean.getCount(),this.repeatTimes - 1);
                        if (cdfBean.getCount() == this.repeatTimes - 1) {
                            //进入围栏或出围栏报警
                            DeviceAlarm deviceAlarm = new DeviceAlarm();
                            deviceAlarm.setDeviceId(device.getId());
                            //类型为SOS
                            deviceAlarm.setAlarmCate(
                                    inPolyon == EnumDeviceFenceStatus.FENCEIN.getCode() ? DeviceAlarmCate.FENCEIN.getCode() : DeviceAlarmCate.FENCEOUT.getCode());
                            //DeviceChannel dc = channelService.getChannelFromDevAndChnCode(device.getId(),gpsMsgInfo.getCode());
                            String desc = inPolyon == EnumDeviceFenceStatus.FENCEIN.getCode() ? DeviceAlarmCate.FENCEIN.getDesc() : DeviceAlarmCate.FENCEOUT.getDesc();
                            log.info("##=====>>> YYYYYYYYY++++ {}: {} =>{},{}",index,desc, gpsMsgInfo.getLongitude(),gpsMsgInfo.getLatitude());

                            DeviceChannel dc = channelService.getChannelFromDevAndChnCode(device.getId(), gpsMsgInfo.getDeviceCode());
                            if (dc != null) {
                                deviceAlarm.setChannelId(dc.getId());
                            }
                            deviceAlarm.setAlarmPriority("1");
                            deviceAlarm.setAlarmTime(gpsMsgInfo.getTime());
                            deviceAlarm.setCreateTime(new Date());
                            deviceAlarm.setUpdateTime(new Date());
                            deviceAlarm.setAlarmDescription(inPolyon == EnumDeviceFenceStatus.FENCEIN.getCode() ? DeviceAlarmCate.FENCEIN.getDesc() : DeviceAlarmCate.FENCEOUT.getDesc());
                            deviceAlarm.setLongitude(gpsMsgInfo.getLongitude());
                            deviceAlarm.setLatitude(gpsMsgInfo.getLatitude());
                            deviceAlarm.setAlarmEvent(inPolyon);
                            deviceAlarmService.save(deviceAlarm);

                            //围栏报警数据
                            FenceAlarm fenceAlarm = new FenceAlarm();
                            fenceAlarm.setAlarmTime(gpsMsgInfo.getTime());
                            fenceAlarm.setFenceId(cdfBean.getFenceId());
                            fenceAlarm.setDeviceId(device.getId());
                            fenceAlarm.setCreateTime(new Date());
                            fenceAlarm.setUpdateTime(new Date());
                            fenceAlarm.setAlarmType(inPolyon);
                            fenceAlarm.setLongitude(gpsMsgInfo.getLongitude());
                            fenceAlarm.setLatitude(gpsMsgInfo.getLatitude());
                            fenceAlarmService.save(fenceAlarm);

                            //先写数据库再查询
                            Thread.sleep(50L);
                            //加入推送队列
                            //log.info("##=====>>> 是加入推送队列:{},{}",device.getUserId(),device.getOrgId());
                            if (device.getUserId() != null || device.getOrgId() != null) {
                            //if (1==0) {
                                    //查询完整信息
                                    QueryWrapper<DeviceAlarmParam> queryWrapper = new QueryWrapper<DeviceAlarmParam>().eq("a.id", deviceAlarm.getId());
                                    DeviceAlarmBean deviceAlarmBean = deviceAlarmService.getOneInfo(queryWrapper, false);
                                    QueryWrapper<Fence> fenceQueryWrapper = new QueryWrapper<>();
                                    fenceQueryWrapper.eq("id", cdfBean.getFenceId());
                                    Fence fence = fenceService.getOne(fenceQueryWrapper, false);
                                    String msg = String.format("%s %s %s 的设备(%s)在(%f,%f)位置 %s <%s> 时报警",
                                            DateUtils.format(deviceAlarm.getAlarmTime(), "yyyy-MM-dd HH:mm:ss"),
                                            deviceAlarmBean.getShortName(),
                                            deviceAlarmBean.getUserName(),
                                            deviceAlarmBean.getDeviceCode(),
                                            deviceAlarmBean.getLongitude(),
                                            deviceAlarmBean.getLatitude(),
                                            deviceAlarmBean.getAlarmCateDesc(),
                                            fence.getFenceName());
                                    UpdateWrapper<DeviceAlarm> updateWrapper = new UpdateWrapper<>();
                                    updateWrapper.set("alarm_message", msg).eq("id", deviceAlarm.getId());
                                    deviceAlarmService.update(updateWrapper);

                                    deviceAlarmBean.setAlarmMessage(msg);
                                    MessageBean messageBean = new MessageBean(EnumSseMessageType.ALARM_NOTIFY);
                                    messageBean.setMessage(deviceAlarmBean);

                                    if (device.getUserId() != null) {
                                        User user = userService.getById(device.getUserId());
                                        messageBean.setUserId(user.getId());
                                        messageBean.setUserName(user.getName());
                                    }
                                    if (device.getOrgId() != null) {
                                        Organization organization = organizationService.getById(device.getOrgId());
                                        messageBean.setOrgId(organization.getId());
                                        messageBean.setOrgPath(organization.getPath());
                                    }

                                    deviceAlarmQueue.offer(messageBean);

                            }
                        }
                        cdfBean.setTime(new Date());
                        if (cdfBean.getCount() == 0) {
                            cdfBean.setCount(this.repeatTimes); //下一次就跳过不会报警了
                        } else {
                            cdfBean.setCount(cdfBean.getCount() + 1);
                        }
                        log.info("##=====>>> 一次就跳过不会报警了:{}",cdfBean.getCount());
                        fenceService.setDeviceFenceStatusToCache(cdfBean);
                    }
                }
            }
        }
    }
}
